/*

  Copyright (C) 2010  INRIA, Planète Team

  Authors:
  ------------------------------------------------------------
   Amir Krifa                        :  krifa.amir@gmail.com
   Chadi Barakat                     :  Chadi.Barakat@sophia.inria.fr
  ------------------------------------------------------------

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 3
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifndef BUNDLES_H
#define BUNDLES_H

#include <stdlib.h>
#include <map>
#include <semaphore.h>


class Handlers;
class Bundle;
class XMLTree;


class Bundles
{

public:
	/**
	 * Constructor.
	 * 
	 * @param The router object, i.e. HBSD_Router.
	 */

	Bundles(Handlers *router);
	~Bundles();
	/**
	 * Called when a bundle_report is received. Create a bundle from
	 * an element in the bundle report.
	 * 
	 * @param event XMLTree object of the <bundle> element within the
	 *    bundle_report.
	 * @return The created bundle object, or null if not created or it
	 *    previously existed.
	 */
	Bundle *newBundleReported(XMLTree *event);
	
	/**
	 * Called to create a new bundle given a "bundle_received_event." The
	 * bundle is created, initialized, and added to the hash table.
	 * 
	 * @param evtBundleRcvd Root of bundle_received_event element.
	 * @return Created bundle; null if a bundle with this id already exists. 
	 */
	Bundle *newBundle(XMLTree *evtBundleRcvd);
	
	/**
	 * Called to initialize a bundle that has been injected. Though we use a
	 * Bundle object, we do not keep track of it like we do other received
	 * bundles.
	 * 
	 * @param evtBundleInjected Root of the bundle_injected_event element.
	 * @return The created bundle.
	 */
	Bundle *newInjectedBundle(XMLTree *evtBundleInjected);
	
	/**
	 * Called to initialize a new bundle that has been received and is
	 * intended for the router. We retain no knowledge of these bundles
	 * (though the policy manager may when it gets invoked).
	 */
	Bundle *newBundleDelivery(XMLTree* evtBundleDelivery);
	/**
	 * Gets the bundle given its GBOF hash key.
	 * 
	 * @param gbofKey GBOF hash key.
	 * @return Bundle object if found, else null.
	 */
	Bundle *getByKey(std::string gbofKey);
	
	
	/**
	 * Called to expire a bundle. Remove knowledge of the bundle from our
	 * tables.
	 * 
	 * Note: The bundles are automatically removed from the data store by
	 * DTN so no additional interaction is required.
	 * 
	 * @param localId local_id of expired bundle.
	 * @return bundle Object representing the expired bundle.
	 */
	Bundle *expire(long localId);
		
	/**
	 * Called to remove a specific bundle. Removing involves losing
	 * knowledge of the bundle, but not sending a delete request to DTN.
	 * 
	 * @param bundle Bundle to be removed.
	 * @return True if the bundle had been active and therefore removed.
	 */
	bool remove(Bundle *bundle);
	
	/**
	 * Called to delete a specific bundle. This causes a delete request
	 * to also be sent.
	 * 
	 * @param bundle Bundle to be deleted.
	 * @return True if the bundle had been active and therefore deleted.
	 */
	bool deleteBundle(Bundle *bundle);
	/**
	 * Called to delete a bundle. This differs from delete in that here a
	 * delete request is sent to DTN even if the bundle manager does not
	 * know about the bundle, such as with an injected bundle.
	 * 
	 * @param bundle The bundle that is to be deleted.
	 */
	void finished(Bundle *bundle);
	
	/**
	 * Determines if a bundle is current, which is simply its presence
	 * in the active std::map table. A bundle will be removed when it expires
	 * or at the discretion of the policy manager.
	 * 
	 * @param localId local_id to look for.
	 * @return True if current, else false.
	 */
	bool isCurrent(long localId);
	
	/**
	 * Given a local id, return the bundle object.
	 * 
	 * @param localId Local id assigned by DTN to the bundle.
	 * @return Bundle object, or null if not found.
	 */
	Bundle *bundlefromLocalId(long localId);
	/**
	 * Called when a "bundle_expired_event" is received. Results in the
	 * bundle being removed from our hash of bundles. Note that links
	 * elsewhere may still reference the bundle.
	 *  
	 * @param evtBundleExpired XML element.
	 * @return bundle The expired bundle.
	 */
	Bundle *eventExpired(XMLTree* evtBundleExpired);
	
	/**
	 * Called when a "bundle_delivered_event" is received. Locates the
	 * corresponding bundle object.
	 *  
	 * @param evtBundleDelivered XML element.
	 * @return Bundle that was delivered. This can be null if the bundle is
	 *    a meta data bundle.
	 */
	Bundle *eventDelivered(XMLTree* evtBundleDelivered);

protected: 

	Handlers *router;
	
private:

	/**
	 * Extracts the local_id attribute from an XML element.
	 * 
	 * @param element XML element containing the attribute.
	 * @return The local_id as a long.
	 * @throws NoSuchElementException
	 */
	long xmlLocalId(XMLTree* element);

	const static int DEFAULT_ACTIVE_CAPACITY = 384;
	
	std::map <std::string,Bundle *> activeBundles;
	std::map <long, std::string> localidGBOFMap;
	sem_t bundlesLock;
	/**
	 * Determines if a bundle already exists. Obviously, this does not
	 * apply to injected bundles or bundles destined for the router.
	 * 
	 * @param evtBundleRcvd "bundle_received_event" XML message.
	 * @return True if it already exists, else false.
	 */
	bool alreadyExists(XMLTree* evtBundleRcvd);
	
	/**
	 * Add the bundle to the active hash table if it doesn't already exist.
	 *  
	 * @param createdBundle The new bundle that is to be added.
	 * @param localId Bundle's local_id: the hash key.
	 * @return The created bundle, or null if already known.
	 */
	Bundle *addIfNew(Bundle *createdBundle, long localId);


};

#endif

